package IC.Symbols;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: nimrod
 * Date: 10/01/12
 * Time: 14:05
 * To change this template use File | Settings | File Templates.
 */
public class SymbolTable {
    private SymbolTable parent;
    private String name;
    private SymbolTableKind kind;
    private Map<String, Symbol> entries;
    private List<SymbolTable> childTables;
    private boolean isStatic;

    public SymbolTable(SymbolTableKind kind, String name, SymbolTable parent) {
        this.kind = kind;
        this.name = name;
        this.parent = parent;
        
        this.entries = new LinkedHashMap<String, Symbol>();
        this.childTables = new ArrayList<SymbolTable>();

        if (parent != null) {
            parent.addChildTable(this);
            this.isStatic = parent.isStatic;
        }
    }

    public SymbolTable getParent() {
        return parent;
    }

    public String getName() {
        return name;
    }

    public SymbolTableKind getKind() {
        return kind;
    }

    public Map<String, Symbol> getEntries() {
        return entries;
    }

    public void setParent(SymbolTable parent) {
        this.parent = parent;
    }

    public void addChildTable(SymbolTable child) {
        this.childTables.add(child);
    }

    public void addSymbol(Symbol sym) {
        this.entries.put(sym.getName(), sym);
    }

    public List<SymbolTable> getChildTables() {
        return childTables;
    }

    public boolean isStatic() {
        return isStatic;
    }

    public void setStatic(boolean aStatic) {
        isStatic = aStatic;
    }

    public Symbol search(String name, boolean parentSearch) {
        return search(name, parentSearch, SearchMode.Default);
    }

    public Symbol search(String name, boolean parentSearch, SearchMode searchMode) {
        Symbol res = this.entries.get(name);

        if (searchMode == SearchMode.Default) {
            searchMode = SearchMode.All;
            if (isStatic) {
                searchMode = SearchMode.StaticOnly;
            }
        }

        if (res != null &&
                (searchMode == SearchMode.All ||
                (searchMode == SearchMode.StaticOnly && res.getKind().isStaticSearchable()) ||
                (searchMode == SearchMode.NonStaticOnly && !res.getKind().isStaticSearchable()))
           )
        {
            return res;
        }

        if (parentSearch && this.parent != null) {
            return parent.search(name, parentSearch, searchMode);
        }

        return null;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        this.writeAsString(sb);
        return sb.toString();
    }

    public void writeAsString(StringBuilder sb) {
        sb.append(this.kind.getName());
        sb.append(" Symbol Table");
        if (this.kind == SymbolTableKind.Block) {
            sb.append(" ( located in ");
            sb.append(this.parent.getName());
            sb.append(" )\n");
        } else {
            sb.append(": ");
            sb.append(this.getName());
            sb.append('\n');
        }

        for (Symbol sym : this.entries.values()) {
            if (sym.getKind() != Kind.This && sym.getKind() != Kind.ReturnVar) {
                sb.append("    ");
                sym.writeAsString(sb);
                sb.append('\n');
            }
        }

        if (this.childTables.size() > 0) {
            sb.append("Children tables: ");
            String sep = "";
            for (SymbolTable child : this.childTables) {
                sb.append(sep);
                sb.append(child.getName());
                sep = ", ";
            }
            sb.append('\n');

            for (SymbolTable child : this.childTables) {
                sb.append('\n');
                child.writeAsString(sb);
            }
        }
    }

    public enum SearchMode {
        StaticOnly,
        NonStaticOnly,
        All,
        Default
    }
}
